<template>
  <div class="ai-chat-page">
    <div class="chat-container">
      <!-- 顶部操作栏 -->
      <div class="chat-header">
        <h3 class="chat-title">蜗牛AI助手</h3>
        <div class="chat-actions">
          <el-button
              type="text"
              size="large"
              icon="el-icon-delete"
              @click="clearMessages"
          >
            清空对话
          </el-button>
        </div>
      </div>

      <!-- 聊天消息区域 -->
      <el-scrollbar class="messages-area">
        <div
            v-for="(msg, index) in messages"
            :key="index"
            :class="['message-bubble', msg.role]"
        >
          <!-- 头像容器 -->
          <div class="avatar-container">
            <el-avatar :src="msg.role === 'user' ? userStore.userInfo.avatarUrl : aiAvatar" />
            <!-- 昵称显示在头像下方 -->
            <div class="nickname">
              {{ msg.role === 'user' ? userStore.userInfo.userName : '蜗牛AI助手' }}
            </div>
          </div>

          <!-- 消息内容块 -->
          <div class="message-content">
            <!-- 消息内容 -->
            <div class="content" v-html="renderMarkdown(msg.content)"></div>
            <div class="timestamp">{{ msg.time }}</div>

            <!-- 加载动画 - 改为绝对定位，覆盖在内容上方 -->
            <div
                v-if="msg.role === 'ai' && msg.isLoading"
                class="loading-overlay"
            >
              <div class="loading-container">
                <div class="loading-dots">
                  <div></div>
                  <div></div>
                  <div></div>
                </div>
                <div class="loading-text">AI正在思考...</div>
              </div>
            </div>
          </div>
        </div>
      </el-scrollbar>

      <!-- 输入区域 -->
      <div class="input-area">
        <el-input
            v-model="inputMessage"
            placeholder="输入消息..."
            type="textarea"
            :rows="3"
            resize="none"
            @keyup.enter.native="sendMessage"
        >
        </el-input>
      </div>
    </div>
  </div>
</template>

<script setup>
import {ref, reactive, nextTick, onMounted} from 'vue'
import {marked} from 'marked'
import DOMPurify from 'dompurify'
import {userInfo} from "@/stores/userinfo.js";
import chat from "@/api/modules/AiChat.js";

import { getValidAccessToken, refreshAuthToken } from '@/utils/authService';
// 用户信息
const userStore = userInfo()
// AI信息
const aiAvatar = ref('https://img.neptech.net/i/2025/06/20/gz6llk.webp')

// 消息相关逻辑
const messages = reactive([])
const inputMessage = ref('')
const isSending = ref(false)

// 添加初始问候语
onMounted(() => {
  // 检查是否有历史消息，如果没有则添加初始问候
  if (messages.length === 0) {
    addInitialGreeting()
  }
})

// 添加初始问候语的方法
const addInitialGreeting = () => {
  // 添加AI初始问候
  messages.push({
    role: 'ai',
    content: userStore.userInfo.nickname + '你好！我是蜗牛AI助手，是一个专业的网络安全靶场考试AI助手，我可以回答各种问题并与你交流。请问有什么我可以帮助你的吗？',
    time: new Date().toLocaleTimeString()
  })

  // 滚动到底部
  nextTick(() => {
    const scrollContainer = document.querySelector('.messages-area .el-scrollbar__wrap')
    scrollContainer.scrollTop = scrollContainer.scrollHeight
  })
}

const sendMessage = async () => {
  if (!inputMessage.value.trim() || isSending.value) return

  // 添加用户消息
  messages.push({
    role: 'user',
    content: inputMessage.value,
    time: new Date().toLocaleTimeString(),
    avatar: userStore.userInfo.avatarUrl,
  })

  // 滚动到底部
  nextTick(() => {
    const scrollContainer = document.querySelector('.messages-area .el-scrollbar__wrap')
    scrollContainer.scrollTop = scrollContainer.scrollHeight
  })

  // 添加AI加载状态消息
  const loadingMsg = {
    role: 'ai',
    content: '',
    isLoading: true
  }
  messages.push(loadingMsg)

  const userMessage = inputMessage.value
  inputMessage.value = ''
  isSending.value = true

  try {
    // 构建请求URL
    const url = new URL(import.meta.env.VITE_API_URL + '/ai/chat');
    url.searchParams.append('userId', userStore.userInfo.userId);
    url.searchParams.append('userInput', userMessage);

    // 使用带重试机制的fetch请求
    const response = await fetchWithRetry(url, {
      method: 'GET',
      headers: {
        'Accept': 'text/event-stream', // 明确指定接收流式响应
      }
    });
    console.log(userStore.userInfo.userId)

    // 创建AI消息条目
    const aiMsg = {
      role: 'ai',
      content: '',
      time: new Date().toLocaleTimeString()
    };
    // 移除加载状态消息，添加正式AI消息
    messages.splice(messages.indexOf(loadingMsg), 1, aiMsg)

    // 读取流式响应
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      // 处理SSE格式的响应
      const chunk = decoder.decode(value, { stream: true });
      const lines = chunk.split('\n');

      for (const line of lines) {
        if (line.startsWith('data:')) {
          const data = line.substring(5).trim();
          if (data) {
            // 检查是否是错误消息
            if (data.startsWith('{') && data.includes('"code":401')) {
              throw new Error('Token expired after refresh');
            }
            aiMsg.content += data;
            // 触发视图更新
            messages.splice(messages.indexOf(aiMsg), 1, { ...aiMsg });
          }
        }
      }

      // 滚动到底部
      nextTick(() => {
        const scrollbar = document.querySelector('.messages-area .el-scrollbar__wrap')
        scrollbar.scrollTop = scrollbar.scrollHeight
      })
    }
  } catch (error) {
    console.error('AI请求错误:', error);
    loadingMsg.content = error.message || '请求服务失败，请稍后重试';
    loadingMsg.isLoading = false;
    messages.splice(messages.indexOf(loadingMsg), 1, { ...loadingMsg });
  } finally {
    isSending.value = false;
  }
}

// 添加带重试机制的fetch封装
const fetchWithRetry = async (url, options, retryCount = 1) => {
  try {
    // 获取有效的访问令牌
    const token = await getValidAccessToken();
    if (!token) {
      throw new Error("用户未登录或Token已过期");
    }

    // 设置认证头
    options.headers = { ...options.headers, 'Authorization': token };

    const response = await fetch(url, options);

    // 对于流式响应，无法提前解析JSON，需要特殊处理
    if (response.headers.get('content-type')?.includes('text/event-stream')) {
      // 检查HTTP状态码
      if (response.status === 401 && retryCount > 0) {
        return handleTokenRefreshAndRetry(url, options, retryCount);
      }
      return response;
    }

    // 非流式响应，按常规方式处理
    const data = await response.json();
    if (response.ok && data.code === 401 && retryCount > 0) {
      return handleTokenRefreshAndRetry(url, options, retryCount);
    }

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    response.parsedData = data;
    return response;
  } catch (error) {
    console.error("Fetch error:", error);
    throw error;
  }
};

// 抽取令牌刷新和重试逻辑为独立函数
const handleTokenRefreshAndRetry = async (url, options, retryCount) => {
  // 刷新令牌
  const newToken = await refreshAuthToken();
  // 更新请求头
  options.headers.Authorization = newToken;
  // 重试请求（减少重试计数）
  return fetchWithRetry(url, options, retryCount - 1);
};

// 清空对话
const clearMessages = () => {
  // 显示确认对话框
  ElMessageBox.confirm(
      '确定要清空所有对话内容吗？此操作不可恢复。',
      '提示',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }
  ).then(() => {
    // 用户点击确定后清空消息
    messages.splice(0)
    // 添加初始问候语
    addInitialGreeting()
    // 向服务器发送清空请求
    chat.clearHistory(userStore.userInfo.userId)
  }).catch(() => {
    // 用户取消操作
  })
}

// Markdown渲染
const renderMarkdown = (content) => {
  marked.setOptions({breaks: true})
  const formatted = content.replace(/\n/g, "  \n")
  return DOMPurify.sanitize(marked.parse(formatted))
}
</script>

<style scoped>
.ai-chat-page {
  height: 870px;
  padding-top: 1px;
}

.chat-container {
  max-width: 1200px;
  margin: 0 auto;
  border-radius: 2px;
  height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
}

/* 新增顶部操作栏样式 */
.chat-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15px 20px;
  border-bottom: 1px solid;
}

.chat-title {
  margin: 0;
  font-size: 18px;
  font-weight: 500;
}

.chat-actions {
  display: flex;
  gap: 10px;
}

.messages-area {
  flex: 1;
  padding: 20px;
}

.message-bubble {
  display: flex;
  margin-bottom: 20px;
  max-width: 80%;
  align-items: flex-start;
}

.message-bubble.user {
  margin-left: auto;
  flex-direction: row-reverse;
}

.avatar-container {
  margin-right: 8px;
  text-align: center;
  flex-shrink: 0;
}

.avatar-container .nickname {
  margin-top: 5px;
  font-size: 12px;
  white-space: nowrap;
  font-weight: bold;
}

.message-content {
  border-radius: 8px;
  padding: 16px;
  position: relative;
  max-width: 600px;
  min-height: 40px;
  min-width: 85px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.message-content .content {
  line-height: 1.6;
  margin-bottom: 10px;
}

.message-content .timestamp {
  position: absolute;
  bottom: 8px;
  right: 12px;
  font-size: 12px;
}

.input-area {
  padding: 20px;
}

/* 加载动画样式 */
.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border-radius: 8px;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 10;
  animation: fadeIn 0.3s ease-out forwards;
}

.loading-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px;
}

.loading-dots {
  display: flex;
  gap: 6px;
  margin-bottom: 12px;
}

.loading-dots div {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: #409eff;
  animation: bounce 1.4s infinite ease-in-out both;
  transform-origin: center bottom;
}

.loading-dots div:nth-child(1) {
  animation-delay: -0.32s;
}

.loading-dots div:nth-child(2) {
  animation-delay: -0.16s;
}

.loading-dots div:nth-child(3) {
  animation-delay: 0s;
}

.loading-text {
  font-size: 14px;
  color: #606266;
  font-weight: 500;
  letter-spacing: 0.5px;
}

@keyframes bounce {
  0%, 80%, 100% {
    transform: scale(0.4);
    opacity: 0.4;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.message-content.skeleton {
  background: linear-gradient(90deg, #f5f7fa 25%, #e6e8eb 50%, #f5f7fa 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}

@keyframes shimmer {
  0% {
    background-position: -200% 0
  }
  100% {
    background-position: 200% 0
  }
}
</style>